package mspbots.timedoctor.client.oauth;

import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.log4j.Log4j2;
import mspbots.timedoctor.client.core.client.ClientUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * com.mspbots.doctor.core.AuthService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2020/3/19
 */
@Log4j2
@Service
public class V1AuthService extends BaseAuthService {

    private final TimeDoctorProperties doctorProperties;
    private final WebClient baseClient;

    public V1AuthService(TimeDoctorProperties doctorProperties,
                         WebClient.Builder builder) {
        this.doctorProperties = doctorProperties;
        this.baseClient = builder.baseUrl("https://webapi.timedoctor.com/")
                .defaultHeader("Accept", "application/json")
                .build();
    }

    @Override
    public Mono<String> authToken(Map<String, Object> params) {
        params.put("grant_type", "authorization_code");
        params.put("redirect_uri", this.doctorProperties.getRedirectUri() + "?tenantId=" + params.get("tenantId"));
        return this.tokenClientGet(params)
                .map(AuthorizationCode::getAccessToken)
                .onErrorResume(err -> Mono.just(err.getMessage()));
    }

    @Override
    protected Mono<String> refreshToken(Long tenantId, AuthorizationCode authorizationCode) {
        Map<String, Object> params = new HashMap<>(6);
        params.put("grant_type", "refresh_token");
        params.put("refresh_token", authorizationCode.getRefreshToken());
        params.put("tenantId", tenantId);
        return this.tokenClientGet(params).map(AuthorizationCode::getAccessToken);
    }

    @Override
    public Mono<Boolean> test(Map<String, Object> params) {
        return null;
    }

    private Mono<AuthorizationCode> tokenClientGet(Map<String, Object> params) {
        params.put("client_id", this.doctorProperties.getClientId());
        params.put("client_secret", this.doctorProperties.getSecretKey());
        return this.baseClient.get().uri(uriBuilder -> uriBuilder.path("/oauth/v2/token").queryParams(ClientUtils.form(params)).build())
                .retrieve()
                .bodyToMono(JsonNode.class)
                .map(jsonNode -> AuthorizationCode.builder().accessToken(jsonNode.get("access_token").asText())
                        .refreshToken(jsonNode.get("refresh_token").asText())
                        .expiresIn(jsonNode.get("expires_in").asLong())
                        .tokenType(jsonNode.get("token_type").asText())
                        .build())
                .doOnNext(authCode -> {
                    this.reactiveRedisTemplate.opsForValue()
                            .set(this.REDIS_KEY_PREFIX + this.REDIS_TOKEN_KEY_PREFIX + params.get("tenantId") + ":authorization",
                                    authCode).subscribe();
                    this.reactiveRedisTemplate.opsForValue()
                            .set(this.REDIS_KEY_PREFIX + this.REDIS_TOKEN_KEY_PREFIX + params.get("tenantId") + ":token",
                                    authCode.getAccessToken(), Duration.ofSeconds(authCode.getExpiresIn() - 2000))
                            .subscribe();
                });
    }

}
